package com.yootk.common.util; // common一般描述公共类

import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload2.FileItem;
import org.apache.commons.fileupload2.disk.DiskFileItemFactory;
import org.apache.commons.fileupload2.servlet.ServletFileUpload;

import java.io.*;
import java.util.*;

public class ParameterUtil {
    public static final long MAX_SIZE = 3145728L; // 表单允许的最大上传数量为3M
    public static final long FILE_MAX_SIZE = 1048576L; // 表示单个文件允许上传的最大长度，本次为1M
    public static final String DEFAULT_TEMP_DIR = "/temp"; // 设置临时的目录
    public static final String DEFAULT_UPLOAD_DIR = "/upload"; // 设置临时的目录
    public static final String DEFAULT_ENCODING = "UTF-8"; // 设置参数的接收编码
    private HttpServletRequest request; // HttpServletRequest请求
    private String uploadFile; // 上传目录
    private String tempFile; // 临时目录
    private String encoding; // 接收编码
    private long maxSize; // 上传总量
    private long fileMaxSize; // 文件大小限制
    private boolean uploadFlag = false; // 如果现在表单有封装此内容为true，如果没有封装为false
    private ServletFileUpload fileUpload; // FileUpload的核心处理类
    private List<String> tempFileNames = new ArrayList<>(); // 保存临时文件名称
    // 对于普通的文本型的参数保存的是表单提交的内容
    // 如果现在需要进行的是二进制的参数，保存的是存储的文件名称
    private Map<String, String[]> paramMap = new HashMap<>();   // 保存最终的处理结果

    /**
     * 构建一个默认的参数处理操作实例
     *
     * @param request HTTP请求对象
     */
    public ParameterUtil(HttpServletRequest request) {
        this(request, DEFAULT_UPLOAD_DIR, DEFAULT_TEMP_DIR, MAX_SIZE, FILE_MAX_SIZE, DEFAULT_ENCODING);
    }

    /**
     * 构建参数工具类，同时设置上传文件保存的父目录
     *
     * @param request    HTTP请求对象
     * @param uploadFile 文件存储的父目录
     */
    public ParameterUtil(HttpServletRequest request, String uploadFile) {
        this(request, uploadFile, DEFAULT_TEMP_DIR, MAX_SIZE, FILE_MAX_SIZE, DEFAULT_ENCODING);
    }

    /**
     * 获取生成的全部临时文件名称
     *
     * @return 本次请求中所保存在临时目录下的文件名称集合
     */
    public List<String> getTempFileNames() {
        return tempFileNames;
    }

    /**
     * 构建一个上传工具类
     *
     * @param request     HTTP请求对象
     * @param uploadFile  文件存储的父目录
     * @param tempFile    临时存放目录
     * @param maxSize     上传的最大限制
     * @param fileMaxSize 每个文件的最大限制
     * @param encoding    默认读取文字编码
     */
    public ParameterUtil(HttpServletRequest request, String uploadFile, String tempFile, long maxSize, long fileMaxSize, String encoding) {
        this.request = request;  // 保存属性
        if (uploadFile.endsWith("/")) { // 路径处理
            this.uploadFile = uploadFile;
        } else {
            this.uploadFile = uploadFile + "/";
        }
        if (tempFile.endsWith("/")) { // 路径处理
            this.tempFile = tempFile;
        } else {
            this.tempFile = tempFile + "/";
        }
        this.maxSize = maxSize; // 保存属性
        this.fileMaxSize = fileMaxSize; // 保存属性
        this.encoding = encoding; // 保存属性
        this.handleParameter(); // 进行参数的处理
    }

    private void handleParameter() { // 处理请求数据
        if ((this.uploadFlag = this.request.getContentType() != null &&
                this.request.getContentType().startsWith("multipart/form-data"))) {  // 请求表单封装
            DiskFileItemFactory factory = new DiskFileItemFactory(); // 磁盘文件管理工厂
            factory.setRepository(new File(this.tempFile)); // 设置所有上传的临时存储目录
            this.fileUpload = new ServletFileUpload(factory); // 定义上传处理类
            this.fileUpload.setSizeMax(this.maxSize);    // 设置允许上传的总长度限制
            this.fileUpload.setFileSizeMax(this.fileMaxSize); // 设置单个上传文件的大小限制
            if (this.fileUpload.isMultipartContent(this.request)) {   // 判断当前的表单是否属于封装状态
                try {// 请求参数处理
                    Map<String, List<FileItem>> map = this.fileUpload.parseParameterMap(request); // 解析所有的上传参数
                    for (Map.Entry<String, List<FileItem>> entry : map.entrySet()) {
                        String paramName = entry.getKey(); // 获取请求参数的名称
                        List<FileItem> allItems = entry.getValue(); // 获取请求参数的内容
                        String[] values = new String[allItems.size()];   // 保存数据集合
                        int foot = 0; // 数组操作下标
                        for (FileItem item : allItems) { // 迭代所有文件项
                            if (item.isFormField()) {   // 内容为普通文本
                                String value = item.getString(this.encoding); // 获取文本
                                values[foot++] = value; // 保存内容
                            } else {    // 二进制文件
                                String fileName = this.saveTempFile(item); // 临时目录中保存
                                this.tempFileNames.add(fileName); // 用于最后临时文件清空
                                values[foot++] = fileName; // 保存文件并返回文件名称
                            }
                        }
                        this.paramMap.put(paramName, values); // 保存到参数集合
                    }
                } catch (Exception e) {
                }
            }
        }
    }

    private String saveTempFile(FileItem item) throws Exception {   // 保存临时文件并生成文件名称
        if (item.getSize() > 0) {   // 现在有上传的文件
            String fileName = "temp." + this.getUUIDName(item); // 生成文件名称
            String filePath = this.request.getServletContext().getRealPath(this.tempFile) + fileName;  // 文件保存路径
            item.write(new File(filePath)); // 进行文件存储
            item.delete(); // 删除临时文件
            return fileName; // 返回文件名称
        }
        return null; // 没有文件
    }

    public String getUUIDName(FileItem item) {   // 创建UUID名称
        return UUID.randomUUID() + "." + item.getContentType().substring(item.getContentType().lastIndexOf("/") + 1);
    }

    public String getParameter(String paramName) {  // 根据参数名称获取参数内容
        if (this.uploadFlag) {  // 表单封装
            if (this.paramMap.containsKey(paramName)) { // 判断参数是否存在
                return this.paramMap.get(paramName)[0]; // 获取里面的第1个元素
            }
            return null;    // 没有该参数返回null
        }
        return this.request.getParameter(paramName); // 原始方法获取参数
    }

    public String[] getParameterValues(String paramName) {  // 根据参数的名称获取一组参数内容
        if (this.uploadFlag) {  // 表单封装
            if (this.paramMap.containsKey(paramName)) { // 判断参数是否存在
                return this.paramMap.get(paramName); // 获取全部元素
            }
            return null;    // 没有该参数返回null
        }
        return this.request.getParameterValues(paramName); // 原始方法获取参数
    }

    public Set<String> getParameterNames() {    // 返回所有参数的名称
        if (this.uploadFlag) {  // 表单封装
            return this.paramMap.keySet();  // 获取全部key
        }
        return this.request.getParameterMap().keySet(); // 原始方法获取名称
    }

    public Map<String, String[]> getParameterMap() { // 返回参数和内容的Map集合
        if (this.uploadFlag) {  // 表单封装
            return this.paramMap; // 返回Map数据
        }
        return this.request.getParameterMap(); // 原始方法获取
    }

    public List<String> getUUIDName(String paramName) throws Exception { // 获取指定参数的文件
        List<String> uuidNames = new ArrayList<>(); // 保存全部生成的UUID名称
        String fileNames[] = this.paramMap.get(paramName); // 获取保存在临时目录中的文件名称
        for (String fileName : fileNames) {
            uuidNames.add("yootk." + UUID.randomUUID() + "." + fileName.substring(fileName.lastIndexOf(".") + 1));
        }
        return uuidNames;
    }

    public void saveUploadFile(String paramName, List<String> uploadFileNames) throws Exception {
        String fileNames[] = this.paramMap.get(paramName); // 获取保存在临时目录中的文件名称
        for (int x = 0; x < fileNames.length; x++) {
            File srcFile = new File(this.request.getServletContext().getRealPath(this.tempFile) + fileNames[x]);  // 临时文件
            File destFile = new File(this.request.getServletContext().getRealPath(this.uploadFile) + uploadFileNames.get(x));  // 目标文件
            InputStream input = new FileInputStream(srcFile);
            OutputStream output = new FileOutputStream(destFile);
            input.transferTo(output); // 文件拷贝
            input.close();
            output.close();
        }
    }


    public void clean() {
        if (this.tempFileNames != null && this.tempFileNames.size() > 0) {
            for (String fileName : this.tempFileNames) {
                String filePath = this.request.getServletContext().getRealPath(this.tempFile) + fileName;  // 文件保存路径
                File file = new File(filePath);
                if (file.exists()) {
                    file.delete();
                }
            }
        }
    }
}

